﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.MathLib
{
     static partial class MathEx
    {
        public const double TwoPI = Math.PI * 2;
        public const double HalfPI = Math.PI * 0.5;

        internal static double _seed = 1234567;
        public const int Infinity = int.MaxValue;
        public const double NaN = double.NaN;
        public const double MAX_VALUE = double.MaxValue;


        public const double EPSILON = 1E-8;
        public const double EPSILON12 = 1E-12;
        public const double EPSILON10 = 1E-10;
        public const double EPSILON8 = 1E-08;
        public const double EPSILON6 = 1E-06;
        public const double EPSILON4 = 1E-04;

        public const double DEG2RAD = Math.PI / 180;
        public const double RAD2DEG = 180 / Math.PI;

        // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
        public static string GenerateUUID()
        {
            return Guid.NewGuid().ToString().ToLower();
        }

        public static double Clamp(double value, double min, double max)
        {

            return Math.Max(min, Math.Min(max, value));

        }

        // compute euclidean modulo of m % n
        // https://en.wikipedia.org/wiki/Modulo_operation
        public static double EuclideanModulo(double n, double m)
        {

            return ((n % m) + m) % m;

        }

        // Linear mapping from range <a1, a2> to range <b1, b2>
        public static double MapLinear(double x, double a1, double a2, double b1, double b2)
        {

            return b1 + (x - a1) * (b2 - b1) / (a2 - a1);

        }

        // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/
        public static double InverseLerp(double x, double y, double value)
        {

            if (x != y)
            {
                return (value - x) / (y - x);
            }
            else
            {
                return 0;
            }

        }

        // https://en.wikipedia.org/wiki/Linear_interpolation
        public static double Lerp(double x, double y, double t)
        {

            return (1 - t) * x + t * y;

        }

        // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
        public static double Damp(double x, double y, double lambda, double dt)
        {

            return Lerp(x, y, 1 - Math.Exp(-lambda * dt));

        }

        // https://www.desmos.com/calculator/vcsjnyz7x4
        public static double Pingpong(int x, int length = 1)
        {

            return length - Math.Abs(EuclideanModulo(x, length * 2) - length);

        }

        // http://en.wikipedia.org/wiki/Smoothstep
        public static double Smoothstep(double x, double min, double max)
        {

            if (x <= min) return 0;
            if (x >= max) return 1;

            x = (x - min) / (max - min);

            return x * x * (3 - 2 * x);

        }

        public static double Smootherstep(double x, double min, double max)
        {

            if (x <= min) return 0;
            if (x >= max) return 1;

            x = (x - min) / (max - min);

            return x * x * x * (x * (x * 6 - 15) + 10);

        }

        // Random integer from <low, high> interval
        public static int RandInt(int low, int high)
        {

            return low + (int)Math.Floor(Random() * (high - low + 1));

        }

        // Random float from <low, high> interval
        public static double RandFloat(double low, double high)
        {

            return low + MathEx.Random() * (high - low);

        }

        // Random float from <-range/2, range/2> interval
        public static double RandFloatSpread(double range)
        {

            return range * (0.5 - MathEx.Random());

        }

        // Deterministic pseudo-random float in the interval [ 0, 1 ]
        public static double SeededRandom(int s = int.MaxValue)
        {

            if (s != int.MaxValue) _seed = s;

            // Mulberry32 generator

            int t = (int)(_seed += 0x6D2B79F5);

            t = MathEx.Imul(t ^ t >> 15, t | 1);

            t ^= t + MathEx.Imul(t ^ t >> 7, t | 61);

            return ((t ^ t >> 14) >> 0) / 4294967296.0;

        }

        public static double DegToRad(double degrees)
        {

            return degrees * DEG2RAD;

        }

        public static double RadToDeg(double radians)
        {

            return radians * RAD2DEG;

        }

        public static bool IsPowerOfTwo(int value)
        {

            return (value & (value - 1)) == 0 && value != 0;

        }

        public static double CeilPowerOfTwo(double value)
        {

            return Math.Pow(2, Math.Ceiling(Math.Log(value) / MathEx.LN2));

        }

        public static double FloorPowerOfTwo(double value)
        {

            return Math.Pow(2, Math.Floor(Math.Log(value) / MathEx.LN2));

        }



        public static double Denormalize(double value, Array array)
        {
            if (array is double[]) return value;

            if (array is UInt16[]) return value / 65535.0;

            if (array is byte[]) return value / 255.0;

            if (array is Int16[]) return MathEx.Max(value / 32767.0, -1.0);

            if (array is sbyte[]) return MathEx.Max(value / 127.0, -1.0);

            throw new ArgumentException("Invalid component type.");

        }


        public static double Normalize(double value, Array array)
        {

            if (array is double[]) return value;

            if (array is UInt16[]) return Math.Round(value * 65535.0);

            if (array is byte[]) return Math.Round(value * 255.0);

            if (array is Int16[]) return Math.Round(value * 32767.0);

            if (array is sbyte[]) return Math.Round(value * 127.0);

            throw new ArgumentException("Invalid component type.");
        }

        public static bool IsNaN(double val)
        {
            return double.IsNaN(val);
        }
        public static bool IsFinite(double val)
        {
            return double.IsInfinity(val);
        }
        public static bool IsNaN(float val) => float.IsNaN(val);
        public static bool IsFinite(float val) => float.IsInfinity(val);

    }

     static partial class MathEx
    {
        public const double E = Math.E;
        public readonly static double LN10 = Math.Log(10, E);
        public readonly static double LN2 = Math.Log(2, E);
        public readonly static double LOG10E = Math.Log10(E);
        public readonly static double LOG2E = Math.Log(E, 2);
        public const double PI = Math.PI;
        public readonly static double SQRT1_2 = Math.Sqrt(0.5);
        public readonly static double SQRT2 = Math.Sqrt(2);


        public static double Acosh(double x) => Math.Log(x + Math.Sqrt(x * x - 1));
        public static double Asinh(double x) => Math.Log(x + Math.Sqrt(x * x + 1));
        public static double Atanh(double x) => Math.Log(1 + x) / (1 - x) / 2;
        public static double Cbrt() => 0;
        public static int Clz32(int x) => 32 - (int)Log2(x);
        public static double Expm1(double x) => Math.Exp(x) - 1;
        //JavaScript中的Math.fround() 函数用于查找给定Number的最接近的32位单精度浮点表示形式
        public static float Fround(double x) => (float)x;
        public static double Hypot(params double[] values) => Math.Sqrt(values.Sum(v => v * v));
        //Math.imul()函数用于计算作为参数传递给它的两个整数的32位乘法结果
        public static int Imul(int x, int y) => (int)Math.BigMul(x, y);//
        public static double Log1p(double x) => Math.Log(1 + x);
        public static double Log2(double val) => Math.Log(val, 2);
        public static double Max(params double[] values) => values.Max();
        public static int Max(params int[] values) => values.Max();
        public static double Min(params double[] values) => values.Min();
        public static int Min(params int[] values) => values.Min();
        private static Random _random = new Random();
        public static double Random()
        {
            return _random.NextDouble();
        }
        public static int Random(int number)
        {
            return _random.Next(number);
        }
        public static int RandomInt()
        {
            return _random.Next();
        }

        public static int Add(int x, int y)
        {
            if (x == int.MaxValue || y == int.MaxValue || (int.MaxValue - x) <= y)
            {
                return int.MaxValue;
            }

            return x + y;
        }

        /// <summary>
        /// 正数相乘, 目前只考虑的两个值相乘大于int.MaxValue 的情况
        /// </summary>
        /// <param name="x"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public static int Multiply(int x, int scale)
        {
            if (x > 0 && scale > 0 && x * scale < 0)
            {
                return int.MaxValue;
            }
            if (x < 0 && scale < 0 && x * scale < 0)
            {
                return int.MaxValue;
            }
            return x * scale;
        }

        /// <summary>
        /// 模拟js中Date.now()
        /// </summary>
        /// <returns></returns>
        public static long DateNow()
        {
            return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
        }

        public static long PerformanceNow()
        {
            //performance.timing.navigationStart = 1680166452990;
            //js中Date.now() ≈ performance.timing.navigationStart + performance.now()
            return DateNow() - 1680166452990;
        }








        public static double DegreeToRadian(double angle)
        {
            return ((angle * PI) / 180.0);
        }

        public static bool IsEqual(double x, double y, double epsilon = EPSILON8)
        {
            return IsEqualZero(x - y, epsilon);
        }

        public static bool IsEqualZero(double x, double epsilon = EPSILON8)
        {
            return (Math.Abs(x) < epsilon);
        }

        public static double RadianToDegree(double angle)
        {
            return ((angle * 180.0) / PI);
        }


        public static double Frexp(double value, out int exp)
        {
            long bits = BitConverter.DoubleToInt64Bits(value);
            long sign = Math.Sign(bits);
            exp = (int)((bits >> 52) & 0x7FF) - 1022;

            bits &= 0x000FFFFFFFFFFFFFL;
            bits |= 0x3FE0000000000000L;

            return BitConverter.Int64BitsToDouble(bits) * (sign == 0 ? 1.0 : -1.0);
        }
    }
}
